import { uuid } from './uuid'

export const OSSAccessKeyId = 'o6jf22DofRcUAUgz'
export const askey = 'CQgxlwx1yJOnNOrTokcakAKdB27dSF'
export const aliurl = 'https://logibeat-test.oss-cn-hangzhou.aliyuncs.com/' //开发
export const policyText =
  '{"expiration": "2120-01-01T12:00:00.000Z","conditions": [["content-length-range", 0, 104857600]]}'

import * as StringUtil from './CodeUtils'

export class B64HmacSHA1 {
  private static b64pad = '='
  private static chrsz = 8

  static create(key: string, data: string) {
    const binb2b64 = B64HmacSHA1.binb2b64
    const core_hmac_sha1 = B64HmacSHA1.core_hmac_sha1
    return binb2b64(core_hmac_sha1(key, data))
  }

  private static safeAdd(x: number, y: number) {
    const lsw = (x & 0xffff) + (y & 0xffff)
    const msw = (x >> 16) + (y >> 16) + (lsw >> 16)
    return (msw << 16) | (lsw & 0xffff)
  }

  private static core_sha1(x: number[], len: number) {
    const safeAdd = B64HmacSHA1.safeAdd
    const sha1_ft = B64HmacSHA1.sha1_ft
    const sha1_kt = B64HmacSHA1.sha1_kt
    const rol = B64HmacSHA1.rol

    /* append padding */
    x[len >> 5] |= 0x80 << (24 - (len % 32))
    x[(((len + 64) >> 9) << 4) + 15] = len

    const w = Array(80)
    let a = 1732584193
    let b = -271733879
    let c = -1732584194
    let d = 271733878
    let e = -1009589776

    for (let i = 0; i < x.length; i += 16) {
      const olda = a
      const oldb = b
      const oldc = c
      const oldd = d
      const olde = e

      for (let j = 0; j < 80; j++) {
        if (j < 16) w[j] = x[i + j]
        else w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1)
        const t = safeAdd(
          safeAdd(rol(a, 5), sha1_ft(j, b, c, d)),
          safeAdd(safeAdd(e, w[j]), sha1_kt(j))
        )
        e = d
        d = c
        c = rol(b, 30)
        b = a
        a = t
      }

      a = safeAdd(a, olda)
      b = safeAdd(b, oldb)
      c = safeAdd(c, oldc)
      d = safeAdd(d, oldd)
      e = safeAdd(e, olde)
    }
    return [a, b, c, d, e]
  }

  private static rol(num: number, cnt: number) {
    return (num << cnt) | (num >>> (32 - cnt))
  }

  private static sha1_ft(t: number, b: number, c: number, d: number) {
    if (t < 20) return (b & c) | (~b & d)
    if (t < 40) return b ^ c ^ d
    if (t < 60) return (b & c) | (b & d) | (c & d)
    return b ^ c ^ d
  }

  private static sha1_kt(t: number) {
    return t < 20
      ? 1518500249
      : t < 40
        ? 1859775393
        : t < 60
          ? -1894007588
          : -899497514
  }

  private static binb2b64(binarray: number[]) {
    const tab =
      'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    let str = ''
    const b64pad = B64HmacSHA1.b64pad
    for (let i = 0; i < binarray.length * 4; i += 3) {
      const triplet =
        (((binarray[i >> 2] >> (8 * (3 - (i % 4)))) & 0xff) << 16) |
        (((binarray[(i + 1) >> 2] >> (8 * (3 - ((i + 1) % 4)))) & 0xff) << 8) |
        ((binarray[(i + 2) >> 2] >> (8 * (3 - ((i + 2) % 4)))) & 0xff)
      for (let j = 0; j < 4; j++) {
        if (i * 8 + j * 6 > binarray.length * 32) str += b64pad
        else str += tab.charAt((triplet >> (6 * (3 - j))) & 0x3f)
      }
    }
    return str
  }

  private static str2binb(str: string, chrsz: number) {
    const bin: number[] = []
    const mask = (1 << chrsz) - 1
    for (let i = 0; i < str.length * chrsz; i += chrsz)
      bin[i >> 5] |=
        (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - (i % 32))
    return bin
  }

  private static core_hmac_sha1(key: string, data: string) {
    const str2binb = B64HmacSHA1.str2binb
    const core_sha1 = B64HmacSHA1.core_sha1
    const chrsz = B64HmacSHA1.chrsz

    let bkey = str2binb(key, chrsz)
    if (bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz)

    const ipad = Array(16),
      opad = Array(16)
    for (let i = 0; i < 16; i++) {
      ipad[i] = bkey[i] ^ 0x36363636
      opad[i] = bkey[i] ^ 0x5c5c5c5c
    }
    const hash = core_sha1(
      ipad.concat(str2binb(data, chrsz)),
      512 + data.length * chrsz
    )
    return core_sha1(opad.concat(hash), 512 + 160)
  }
}

export class UploadFileToAliCloud {
  private path: string

  constructor(path = 'certify') {
    this.path = path
  }

  // 将base64格式转换为blob。仅仅图片上传组件使用
  private _convertBase64UrlToBlob(urlData: string, filename: string) {
    let [metainfo, bytes] = urlData.split(',')[1]
    const mime = metainfo.match(/:(.*?);/)![1]
    bytes = window.atob(bytes)
    const ab = new ArrayBuffer(bytes.length)
    const ia = new Uint8Array(ab)
    for (let i = 0; i < bytes.length; i++) {
      ia[i] = bytes.charCodeAt(i)
    }
    return new File([ab], filename, { type: mime })
  }

  uploadUrlData(urlData: string, filename: string) {
    return this.uploadFile(this._convertBase64UrlToBlob(urlData, filename))
  }

  uploadFile(file: File) {
    // 计算路径
    const formdata = new FormData()
    const pocity = StringUtil.b64_encode(policyText)
    const pathInfo = this.packageFileUrl(file.name)
    formdata.append('OSSAccessKeyId', OSSAccessKeyId)
    formdata.append('policy', pocity)
    formdata.append('Signature', B64HmacSHA1.create(askey, pocity))
    formdata.append('key', pathInfo.key) //上传的路径为/ss.jpg 自己修改
    formdata.append('file', file, pathInfo.filename)
    formdata.append('submit', 'Upload to OSS')
    return this.upload(formdata, pathInfo.url)
  }

  private async upload(formdata: FormData, fileUrl: string) {
    const { ok, statusText } = await fetch(aliurl, {
      method: 'POST',
      body: formdata
    })
    if (ok) {
      return fileUrl
    } else {
      throw new Error(statusText)
    }
  }

  /**
   * @desc 组装上传文件地址
   */
  private packageFileUrl(str: string) {
    const ext = str.split('.').pop()
    const now = new Date()
    const year = now.getFullYear()
    let mouth: number | string = now.getMonth() + 1
    let day: number | string = now.getDate()
    const hour = now.getHours(),
      minute = now.getMinutes()
    mouth = mouth < 10 ? '0' + mouth : mouth
    day = day < 10 ? '0' + day : day
    const filename = `_${hour}${minute}_${uuid()}.${ext}`
    const path = `prod/MegatronWeb/${localStorage.getItem('mobile')}/${
      this.path
    }/${year}${mouth}/${year}${mouth}${day}_`
    let key = path + '${filename}'
    key = path + filename
    const url = aliurl + path + filename
    return {
      filename, // 文件名request用
      key, // request 用
      url // 成功后图片地址
    }
  }
}
